﻿using System;

namespace Quail.Parallel
{
    /// <summary>
    /// Used to pass context information from a <see cref="ParallelTestManager{TShell,TTestData}"/> to it's <see cref="ParallelTestWorker{TShell,TTestData}"/> objects.
    /// </summary>
    /// <typeparam name="TShell">The type of the shell to be used by the worker.</typeparam>
    /// <typeparam name="TTestData">The type of test data contained in the worker queue.</typeparam>
    public class ParallelTestContext<TShell, TTestData>
        where TShell : ITestShell
    {
        private readonly Func<TShell> _getShell;
        private readonly Func<ParallelTestContext<TShell, TTestData>, TTestData> _getNextWorkItem;
        private TShell _shell;

        /// <summary>
        /// Initializes a new instance of the <see cref="ParallelTestContext{TShell,TTestData}"/> class.
        /// </summary>
        /// <param name="testManager">The test manager.</param>
        /// <param name="getNextWorkItem">method to retrieve the next work item</param>
        /// <param name="getShell">A function used to get a started shell for the worker. This allows the Shell propery to be lazy.</param>
        public ParallelTestContext(
            ParallelTestManager<TShell, TTestData> testManager,
            Func<ParallelTestContext<TShell, TTestData>, TTestData> getNextWorkItem,
            Func<TShell> getShell)
        {
            TestManager = testManager;
            _getNextWorkItem = getNextWorkItem;
            _getShell = getShell;
        }

        /// <summary>
        /// Gets the test manager for the context
        /// </summary>
        public ParallelTestManager<TShell, TTestData> TestManager
        {
            get; private set;
        }

        /// <summary>
        /// Gets the shell to be used by the worker.  If the shell is not started, it is started and returned.  Once it is started, the same shell will be returned.
        /// </summary>
        public TShell Shell
        {
            get
            {
                if (Equals(_shell, default(TShell)))
                {
                    _shell = _getShell();
                }

                return _shell;
            }
        }

        /// <summary>
        /// Gets or sets user context for this instance of the test
        /// </summary>
        public object UserContext
        {
            get; set;
        }

        /// <summary>
        /// Gets the method to return the next test data item
        /// </summary>
        /// <returns>The next work item, or null if no more items are available</returns>
        public TTestData GetNextTestData()
        {
            return _getNextWorkItem(this);
        }
    }
}